#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <limits.h>
#include <time.h>
#include <sys/signal.h>
#include <sys/errno.h>
#include <stdlib.h>

/*
 * Constants and Macros
 */

#define FAILURE -1
#define ABS     TIMER_ABSTIME
#define REL     0
#define TIMERS  3

#define MIN(x,y) (((x) < (y)) ? (x) : (y))

void sig_handler();
void timeaddval();
struct sigaction sig_act;

/*
 * Control Structure for Timer Examples
 */
struct timer_definitions {
	int type;                       /* Absolute or Relative Timer */
	struct sigevent evp;            /* Event structure */
	struct itimerspec timeout;      /* Timer interval */
};

/*
 * Initialize timer_definitions array for use in example as follows:
 *
 *   type, { sigev_value, sigev_signo }, { it_iteration, it_value }
 */

struct timer_definitions timer_values[TIMERS] = {
        { ABS, {0, SIGUSR1}, {1, 0, 1, 0} }
};

timer_t timerid[TIMERS];
int timers_available;                   /* number of timers available */
volatile int usr1;
sigset_t mask;

main()
{
	int status, i;
	int clock_id = CLOCK_REALTIME;
	struct timespec current_time;

	/*
	 *  Initialize the sigaction structure for the handler.
	 */

	sigemptyset(&mask);
	sig_act.sa_handler = (void *)sig_handler;
	sig_act.sa_flags = 0;
	sigemptyset(&sig_act.sa_mask);
	usr1 = 0;

	/*
	 *  Determine whether it's possible to create TIMERS timers.
	 *  If not, create TIMER_MAX timers.
	 */

	timers_available = MIN(sysconf(_SC_TIMER_MAX),TIMERS);

	/*
	 * Create "timer_available" timers, using a unique signal
	 * type to denote the timer's expiration. Then initialize
	 * a signal handler to handle timer expiration for the timer.
	 */

	status = timer_create(clock_id, &timer_values[0].evp, &timerid[0]);
	if (status == FAILURE)
	{
		perror("timer_create");
        exit(FAILURE);
	}
	sigaction(timer_values[0].evp.sigev_signo, &sig_act, 0);

	/*
	 * Establish a handler to catch CTRL-c and use it for exiting.
	 */

    sigaction(SIGINT, &sig_act, NULL);      /* catch crtl-c */

	/*
	 * Queue the following Timers: (see timer_values structure for details)
	 *
	 * An absolute periodic timer. (Notification is via SIGUSR1).
	 *
	 * (NOTE: The number of TIMERS queued actually depends on
	 *  timers_available)
	 */

	status = clock_gettime(CLOCK_REALTIME, &current_time);
	timeaddval(&timer_values[0].timeout.it_value, &current_time);
	if (status == FAILURE) 
	{
		perror("timer_gettime failed: ");
		exit(FAILURE);
	}
	
	status = timer_settime(timerid[0], timer_values[0].type, &timer_values[0].timeout, NULL);
	if (status == FAILURE) 
	{
		perror("timer_settime failed: ");
		exit(FAILURE);
	}
	
	while (1)
	{
	}
}

/*
 *  Handle Timer expiration or Program Termination.
 */

void sig_handler(signo)
int signo;
{
	int i, status;

	switch (signo) {
		case SIGUSR1:
			usr1++;
			printf("Timer expired %d\r\n", usr1);
		break;
		case SIGINT:
			for (i = 0; i < timers_available; i++)  /* delete timers */
			{
				status = timer_delete(timerid[i]);
			}
			printf("Program terminate\r\n");
			exit(1);  /* exit if CRTL/C is issued */
	}
	return;
}

/*
 * Add two timevalues: t1 = t1 + t2
 */

void timeaddval(t1, t2)
struct timespec *t1, *t2;
{
        t1->tv_sec += t2->tv_sec;
        t1->tv_nsec += t2->tv_nsec;
        if (t1->tv_nsec < 0) {
                t1->tv_sec--;
                t1->tv_nsec += 1000000000;
        }
        if (t1->tv_nsec >= 1000000000) {
                t1->tv_sec++;
                t1->tv_nsec -= 1000000000;
        }
}
